#pragma once

#include <stdlib.h>

#ifdef WIN32   // Windows system specific
#include <windows.h>
#else          // Unix based system specific
#include <sys/time.h>
#endif


namespace HighResolutionTimer
{
	class Timer
	{
	public:
		Timer();                                    // default constructor
		~Timer();                                   // default destructor

		inline void   start();                      // start timer
		inline void   stop();                       // stop the timer
		inline double getElapsedTimeInSec();        // get elapsed time in second (same as getElapsedTime)
		inline double getElapsedTimeInMilliSec();   // get elapsed time in milli-second
		inline double getElapsedTimeInMicroSec();   // get elapsed time in micro-second
	private:
		double startTimeInMicroSec;                 // starting time in micro-second
		double endTimeInMicroSec;                   // ending time in micro-second
		int    stopped;                             // stop flag 
#ifdef WIN32
		LARGE_INTEGER frequency;                    // ticks per second
		LARGE_INTEGER startCount;                   //
		LARGE_INTEGER endCount;                     //
#else
		timeval startCount;                         //
		timeval endCount;                           //
#endif
	}; 

	inline Timer::Timer()
	{
#ifdef WIN32
		QueryPerformanceFrequency(&frequency);
		startCount.QuadPart = 0;
		endCount.QuadPart = 0;
#else
		startCount.tv_sec = startCount.tv_usec = 0;
		endCount.tv_sec = endCount.tv_usec = 0;
#endif

		stopped = 0;
		startTimeInMicroSec = 0;
		endTimeInMicroSec = 0;
	}

	inline Timer::~Timer()
	{
	}

	inline void Timer::start()
	{
		stopped = 0; // reset stop flag
#ifdef WIN32
		QueryPerformanceCounter(&startCount);
#else
		gettimeofday(&startCount, NULL);
#endif
	}

	inline void Timer::stop()
	{
		stopped = 1; // set timer stopped flag

#ifdef WIN32
		QueryPerformanceCounter(&endCount);
#else
		gettimeofday(&endCount, NULL);
#endif
	}

	inline double Timer::getElapsedTimeInMicroSec()
	{
#ifdef WIN32
		if(!stopped)
			QueryPerformanceCounter(&endCount);

		startTimeInMicroSec = startCount.QuadPart * (1000000.0 / frequency.QuadPart);
		endTimeInMicroSec = endCount.QuadPart * (1000000.0 / frequency.QuadPart);
#else
		if(!stopped)
			gettimeofday(&endCount, NULL);

		startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec;
		endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec;
#endif

		return endTimeInMicroSec - startTimeInMicroSec;
	}

	inline double Timer::getElapsedTimeInMilliSec()
	{
		return this->getElapsedTimeInMicroSec() * 0.001;
	}


	inline double Timer::getElapsedTimeInSec()
	{
		return this->getElapsedTimeInMicroSec() * 0.000001;
	}
}